<!doctype html>
<html>

<head>
  <title>Android Remote Session</title>
  <meta charset="UTF-8" />
  <style>
    .fc {
      display: flex;
      flex-direction: column;
      align-items: stretch;
    }

    .fc>*,
    .fr>* {
      flex: 1;
    }

    .f0 {
      flex: initial !important;
      flex-shrink: 1;
    }

    .fr {
      display: flex;
      flex-direction: row;
      align-items: stretch;
    }

    .bb {
      border: 1px solid black;
    }

    #canvas {
      display: inline-block;
    }

    .off {
      background-color: red;
    }
  </style>
</head>

<body class='fr'>
  <div class='f0'>
    <canvas class='bb' tabindex="0" id="canvas"></canvas>
  </div>
  <div class='fc' id='controls'>
    <div class='fr f0 btnr'>
      <button tabindex="2" id="power">Power</button>
      <button tabindex="3" id="get-clip">Get Clipboard</button>
      <button tabindex="4" id="set-clip">Set Clipboard</button>
    </div>
    <textarea tabindex="5" id="txt-clip" cols='10'></textarea>
    <div class='fr f0 btnr'>
      <button tabindex="6" id="list-ports">Ports</button>
      <input tabindex='7' id='port-num' value='19000,19001,7007' size='1' style='flex:5;' />
      <button tabindex="8" id="fwd-port">Fwd</button>
      <button tabindex="9" id="rev-port">Rev</button>
    </div>
    <div class='bb' style='white-space: pre-wrap;' id='port-list'>&nbsp;</div>
    <div class='fr f0 btnr'>
      <button class='off' tabindex="10" id="minicap">Minicap</button>
    </div>
  </div>
  <script>
    'option strict';
    var debug = false;
    function setdebug() {
      debug = !debug;
      wsJson({ event: 'debug', val: debug });
    }
    /*jshint browser:true*/

    var BLANK_IMG =
      ''

    var canvas = document.getElementById('canvas')
      , g = canvas.getContext('2d')
      , elClts = document.getElementById('controls')
      , resM = 25
      , fcr = (row) => {
        for (var e of document.querySelectorAll('.btnr')) {
          e.classList.remove(row ? 'fc' : 'fr');
          e.classList.add(!row ? 'fc' : 'fr');
        }
      }
      , minW = 0
      , fnRes = () => {
        fcr(elClts.scrollWidth > 310);

        canvas.style.maxHeight = (window.innerHeight - resM) + 'px'
        canvas.style.maxWidth = (window.innerWidth - resM) + 'px'
      }
      , minicap = document.getElementById('minicap')
      , landscape = false;

    fnRes();
    window.onresize = fnRes;

    function wsOnMessage(message) {
      if (typeof message.data === 'string') {
        let j = null;
        try { j = JSON.parse(message.data); } catch (e) { }
        if (j) {
          if (j.type == 'GetClipboardResponse') getClipResponse(j);
          if (j.type == 'ports') getPortsResponse(j);
          if (j.type == 'minicapStatus') {
            if (!j.state) blankScreen();
            console.log(message.data);
          }
        }
      } else {
        var blob = new Blob([message.data], { type: 'image/jpeg' })
        var URL = window.URL || window.webkitURL
        var img = new Image()
        img.onload = function () {
          console.log(img.width, img.height)
          canvas.width = img.width;
          canvas.height = img.height;
          fnOrientation(img.width, img.height);
          g.drawImage(img, 0, 0);
          img.onload = null;
          img.src = BLANK_IMG;
          img = null;
          u = null;
          blob = null;
          fnRes();
          minicap.classList.remove('off');
        }
        var u = URL.createObjectURL(blob)
        img.src = u
      }
    }
    var ws;
    function createWs() {
      ws = new WebSocket('ws://' + window.location.host, 'minicap')
      ws.binaryType = 'blob'

      ws.onclose = function () {
        blankScreen();
        console.log('ws close...try reopen in 2 seconds');
        setTimeout(createWs, 2000);
      }

      ws.onopen = function () { console.log('ws open'); };
      ws.onerror = function () { console.log('ws error', arguments); };

      ws.onmessage = wsOnMessage;

    }
    function wsJson(obj) {
      if (ws.readyState === ws.OPEN) {
        var json = JSON.stringify(obj);
        if (debug) console.log(json);
        ws.send(json);
      } else console.log('websocket is closed');
    }
    createWs();

    function blankScreen() {
      g.fillStyle = "#a00";
      g.fillRect(0, 0, canvas.width, canvas.height);
      minicap.classList.add('off');
    }

    function fnOrientation(w, h) {
      landscape = w / h > 1;
      document.body.classList.remove(!landscape ? 'fc' : 'fr');
      document.body.classList.add(landscape ? 'fc' : 'fr');
    }

    canvas.mdown = false;
    canvas.debounce = 0;
    function mouseHandler(t, e) {
      var cx = e.clientX || (e.touches[0] || e.changedTouches[0] || {}).clientX,
        cy = e.clientY || (e.touches[0] || e.changedTouches[0] || {}).clientY;

      var x = cx / canvas.scrollWidth, y = cy / canvas.scrollHeight;
      if (landscape) { var tmp = x; x = 1 - y; y = tmp; }
      wsJson({ event: 'mouse', t: t, x: x, y: y });
      if (debug) console.log(e.type, t, canvas.mdown, cx, cy, canvas.scrollWidth, canvas.scrollHeight);
    }
    var fnmm = function (e) {
      if (!canvas.mdown) return;
      if (!canvas.debounce) {
        canvas.debounce++;
        mouseHandler('m', e);
        setTimeout(() => {
          canvas.debounce = 0;
        }, 100);
      }
    };
    canvas.addEventListener('mousemove', fnmm);
    canvas.addEventListener('touchmove', fnmm);
    var fnmd = function (e) {
      if (canvas.mdown) return;
      canvas.mdown = true;
      mouseHandler('d', e);
      e.preventDefault();
      canvas.focus();
    };
    canvas.addEventListener('mousedown', fnmd);
    canvas.addEventListener('touchstart', fnmd);
    var fnmu = function (e) {
      if (!canvas.mdown) return;
      canvas.mdown = false;
      mouseHandler('u', e);
      e.preventDefault();
    };
    canvas.addEventListener('mouseup', fnmu);
    canvas.addEventListener('touchend', fnmu);

    function keyHandler(e, isDown) {
      var cl = e.getModifierState('CapsLock');
      var obj = {
        event: 'key', key: e.keyCode,
        mods: [e.shiftKey, e.ctrlKey, e.altKey, e.metaKey, cl], isDown
      }
      wsJson(obj);
    }
    canvas.addEventListener('keydown', function (e) {
      keyHandler(e, true);
      e.preventDefault();
    });
    canvas.addEventListener('keyup', function (e) {
      keyHandler(e, false);
      e.preventDefault();
    });

    var bPower = document.getElementById('power');
    bPower.onclick = function (e) {
      wsJson({ event: 'power' });
    };

    var getClip = document.getElementById('get-clip');
    var txtClip = document.getElementById('txt-clip');
    var setClip = document.getElementById('set-clip');
    getClip.onclick = function (e) {
      txtClip.allowUpdate = true;
      wsJson({ event: 'getclip' });
    };
    setClip.onclick = function (e) {
      wsJson({ event: 'setclip', text: txtClip.value });
    };
    function getClipResponse(j) {
      if (txtClip.allowUpdate) {
        txtClip.value = j.message.text;
        txtClip.allowUpdate = false;
      }
    }

    var lstPorts = document.getElementById('list-ports');
    var portList = document.getElementById('port-list');
    var fwdPort = document.getElementById('fwd-port');
    var revPort = document.getElementById('rev-port');
    var portNum = document.getElementById('port-num');
    lstPorts.onclick = function () {
      wsJson({ event: 'listports' });
    };
    function getPortsResponse(j) {
      portList.innerHTML = j.data;
    }
    fwdPort.onclick = function () {
      wsJson({ event: 'addport', rev: false, port: portNum.value });
    };
    revPort.onclick = function () {
      wsJson({ event: 'addport', rev: true, port: portNum.value });
    };

    minicap.onclick = function () {
      wsJson({ event: 'minicap' });
    };
  </script>
</body>

</html>